use axum::Json;
use axum::extract::Query;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use serde::{Deserialize, Serialize};
use sq_crypto::{
    base64_decode, do_decrypt, do_encrypt, get_random_str, make_sign, parse_decrypted,
    verify_msg_signature,
};
use std::borrow::Cow;
use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Deserialize)]
pub struct HookParam<'a> {
    pub signature: Cow<'a, str>,
    pub timestamp: Cow<'a, str>,
    pub nonce: Cow<'a, str>,
    #[serde(rename = "echostr")]
    pub echo_str: Cow<'a, str>,
}

#[derive(Deserialize)]
pub struct RecvPlainQuery {
    signature: String,
    timestamp: u64,
    nonce: String,
}

#[derive(Deserialize, Debug)]
pub struct RecvQuery<'a> {
    pub signature: Cow<'a, str>,
    pub timestamp: Cow<'a, str>,
    pub nonce: Cow<'a, str>,
    pub openid: Cow<'a, str>,
    pub encrypt_type: Cow<'a, str>,
    pub msg_signature: Cow<'a, str>,
}

#[derive(Deserialize, Debug)]
pub struct RecvBody {
    #[serde(rename = "ToUserName")]
    pub to_user_name: String,
    #[serde(rename = "Encrypt")]
    pub encrypt: String,
}

pub struct DecryptedBody<'a> {
    random: Cow<'a, str>,
    msg_len: usize,
    msg: Cow<'a, str>,
    app_id: Cow<'a, str>,
}

// 解密后的消息结构
#[derive(Deserialize, Debug)]
pub struct DecryptedMessage {
    #[serde(rename = "ToUserName")]
    to_user_name: String,
    #[serde(rename = "FromUserName")]
    from_user_name: String,
    #[serde(rename = "CreateTime")]
    create_time: u64,
    #[serde(rename = "MsgType")]
    msg_type: String,
    #[serde(rename = "Event")]
    event: Option<String>,
    debug_str: Option<String>,
}

// 响应体结构
#[derive(Serialize)]
struct WechatResponse {
    #[serde(rename = "Encrypt")]
    encrypt: String,
    #[serde(rename = "MsgSignature")]
    msg_signature: String,
    #[serde(rename = "TimeStamp")]
    timestamp: u64,
    #[serde(rename = "Nonce")]
    nonce: String,
}

pub async fn recv_plain(Query(param): Query<RecvPlainQuery>) -> Response {
    let cli = sq_wecom::cli();
    let token = cli.token();
    tracing::info!("token: {:?}", token);
    let timestamp = param.timestamp.to_string();
    tracing::info!("timestamp: {:?}", timestamp);
    let expected_sign = param.signature;
    tracing::info!("expected_sign: {:?}", expected_sign);
    let verify = verify_msg_signature(vec![token, &timestamp, &param.nonce], &expected_sign);
    match verify {
        true => (StatusCode::OK, "succeed123").into_response(),
        false => (StatusCode::INTERNAL_SERVER_ERROR, "signature verify err！").into_response(),
    }
}

pub async fn recv(Query(param): Query<RecvQuery<'_>>, Json(body): Json<RecvBody>) -> Response {
    tracing::info!("recv body: {:?}", body);
    tracing::info!("recv query: {:?}", param);
    let wecom_cli = sq_wecom::cli();
    let token = wecom_cli.token();
    let timestamp = param.timestamp.as_ref();
    let nonce = param.nonce.as_ref();
    let encrypt = body.encrypt.as_str();
    let seeds = vec![token, timestamp, nonce, encrypt];
    let expected_sign = param.msg_signature.as_ref();

    if !verify_msg_signature(seeds, expected_sign) {
        return (StatusCode::INTERNAL_SERVER_ERROR, "signature verify err！").into_response();
    }

    let encoding_aes_key = wecom_cli.encoding_aes_key();

    let aes_key_bytes = match base64_decode(encoding_aes_key) {
        Ok(data) => data,
        Err(_) => {
            return (StatusCode::INTERNAL_SERVER_ERROR, "aes key decode err!").into_response();
        }
    };

    let tmp_msg = match base64_decode(encrypt) {
        Ok(data) => data,
        Err(_) => {
            return (StatusCode::INTERNAL_SERVER_ERROR, "encrypt msg decode err!").into_response();
        }
    };

    let decrypted_bytes = do_decrypt(&tmp_msg, &aes_key_bytes);

    let (_, _, msg, app_id) = match parse_decrypted(&decrypted_bytes) {
        Ok(tup) => tup,
        Err(_) => {
            return (StatusCode::INTERNAL_SERVER_ERROR, "encrypt msg parse err!").into_response();
        }
    };

    if app_id != wecom_cli.app_id() {
        return (StatusCode::BAD_REQUEST, "app_id mismatch err!").into_response();
    }

    let decrypted_body_result = serde_json::from_str::<DecryptedMessage>(msg.as_ref());
    let decrypted_body = match decrypted_body_result {
        Ok(msg) => msg,
        Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    };

    tracing::info!("decrypted message: {:?}", decrypted_body);

    let random_str = get_random_str();
    let reply_msg = "hello world";
    let encrypted_resp = do_encrypt(&random_str, reply_msg, app_id.as_ref(), encoding_aes_key);
    // 获取秒级时间戳
    let timestamp = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_secs();

    let resp_sign = make_sign(vec![
        wecom_cli.token(),
        &timestamp.to_string(),
        nonce,
        &encrypted_resp,
    ]);

    let resp = WechatResponse {
        encrypt: encrypted_resp,
        msg_signature: resp_sign,
        timestamp,
        nonce: nonce.to_string(),
    };

    let resp_json = serde_json::to_string(&resp).expect("json failed");

    (StatusCode::ACCEPTED, resp_json).into_response()
}

pub async fn callback(Query(param): Query<HookParam<'_>>) -> Response {
    tracing::info!("wecom hook: {:?}", param.signature);

    let client = sq_wecom::cli();
    let expected_sign = param.signature.as_ref();
    let echo_str = param.echo_str;
    let timestamp = param.timestamp.as_ref();
    let nonce = param.nonce.as_ref();
    let token = client.token();

    let seeds = vec![token, timestamp, nonce];

    if !verify_msg_signature(seeds, expected_sign) {
        return (StatusCode::INTERNAL_SERVER_ERROR, "signature verify err！").into_response();
    }
    echo_str.to_string().into_response()
}
